Explore padrões de tipo TypeScript para sanitização de entrada e construa aplicações seguras e confiáveis. Aprenda a prevenir vulnerabilidades como XSS e ataques de injeção.
Segurança TypeScript: Padrões de Tipo de Sanitização de Entrada para Aplicações Robustas
No mundo interconectado de hoje, construir aplicações web seguras e confiáveis é de suma importância. Com a crescente sofisticação das ameaças cibernéticas, os desenvolvedores precisam empregar medidas de segurança robustas para proteger dados sensíveis e prevenir ataques maliciosos. O TypeScript, com seu sistema de tipagem forte, oferece ferramentas poderosas para aprimorar a segurança das aplicações, particularmente através de padrões de tipo de sanitização de entrada. Este guia abrangente explora vários padrões de tipo TypeScript para sanitização de entrada, permitindo que você construa aplicações mais seguras e resilientes.
Por que a Sanitização de Entrada é Crucial
A sanitização de entrada é o processo de limpar ou modificar dados fornecidos pelo usuário para evitar que causem danos à aplicação ou aos seus usuários. Dados não confiáveis, seja de envios de formulários, requisições de API ou qualquer outra fonte externa, podem introduzir vulnerabilidades como:
- Cross-Site Scripting (XSS): Atacantes injetam scripts maliciosos em páginas web visualizadas por outros usuários.
- SQL Injection: Atacantes inserem código SQL malicioso em consultas de banco de dados.
- Command Injection: Atacantes executam comandos arbitrários no servidor.
- Path Traversal: Atacantes acessam arquivos ou diretórios não autorizados.
A sanitização de entrada eficaz mitiga esses riscos garantindo que todos os dados processados pela aplicação estejam em conformidade com os formatos esperados e não contenham conteúdo prejudicial.
Aproveitando o Sistema de Tipos do TypeScript para Sanitização de Entrada
O sistema de tipos do TypeScript oferece várias vantagens para implementar a sanitização de entrada:
- Análise Estática: O compilador do TypeScript pode detectar potenciais erros relacionados a tipos durante o desenvolvimento, antes do tempo de execução.
- Segurança de Tipo: Impõe tipos de dados, reduzindo o risco de formatos de dados inesperados.
- Clareza do Código: Melhora a legibilidade e a manutenibilidade do código através de declarações de tipo explícitas.
- Suporte a Refatoração: Facilita a refatoração do código, mantendo a segurança de tipo.
Ao aproveitar o sistema de tipos do TypeScript, os desenvolvedores podem criar mecanismos robustos de sanitização de entrada que minimizam o risco de vulnerabilidades de segurança.
Padrões Comuns de Tipo de Sanitização de Entrada em TypeScript
1. Sanitização de String
A sanitização de string envolve a limpeza e validação de entradas de string para prevenir XSS e outros ataques de injeção. Aqui estão algumas técnicas comuns:
a. Escapando Entidades HTML
Escapar entidades HTML converte caracteres potencialmente prejudiciais em suas entidades HTML correspondentes, impedindo que sejam interpretados como código HTML. Por exemplo, < torna-se <, e > torna-se >.
Exemplo:
function escapeHtml(str: string): string {
const map: { [key: string]: string } = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return str.replace(/[&<>"']/g, (m) => map[m]);
}
const userInput: string = '';
const sanitizedInput: string = escapeHtml(userInput);
console.log(sanitizedInput); // Output: <script>alert("XSS");</script>
b. Validação por Expressão Regular
Expressões regulares podem ser usadas para validar se uma string está em conformidade com um formato específico, como um endereço de e-mail ou um número de telefone.
Exemplo:
function isValidEmail(email: string): boolean {
const emailRegex: RegExp = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
return emailRegex.test(email);
}
const email1: string = 'test@example.com';
const email2: string = 'invalid-email';
console.log(isValidEmail(email1)); // Output: true
console.log(isValidEmail(email2)); // Output: false
c. Aliases de Tipo para Formatos de String Específicos
Aliases de tipo TypeScript podem ser usados para definir formatos de string específicos e impô-los em tempo de compilação.
Exemplo:
type Email = string & { readonly __email: unique symbol };
function createEmail(input: string): Email {
if (!isValidEmail(input)) {
throw new Error('Invalid email format');
}
return input as Email;
}
try {
const validEmail: Email = createEmail('test@example.com');
console.log(validEmail); // Output: test@example.com (with type Email)
const invalidEmail = createEmail('invalid-email'); //Throws error
} catch (error) {
console.error(error);
}
2. Sanitização de Número
A sanitização de número envolve a validação de que as entradas numéricas estão dentro de faixas aceitáveis e em conformidade com os formatos esperados.
a. Validação de Faixa
Garanta que um número esteja dentro de uma faixa específica.
Exemplo:
function validateAge(age: number): number {
if (age < 0 || age > 120) {
throw new Error('Invalid age: Age must be between 0 and 120.');
}
return age;
}
try {
const validAge: number = validateAge(30);
console.log(validAge); // Output: 30
const invalidAge: number = validateAge(150); // Throws error
} catch (error) {
console.error(error);
}
b. Guardas de Tipo para Tipos Numéricos
Use guardas de tipo para garantir que um valor seja um número antes de realizar operações nele.
Exemplo:
function isNumber(value: any): value is number {
return typeof value === 'number' && isFinite(value);
}
function processNumber(value: any): number {
if (!isNumber(value)) {
throw new Error('Invalid input: Input must be a number.');
}
return value;
}
try {
const validNumber: number = processNumber(42);
console.log(validNumber); // Output: 42
const invalidNumber: number = processNumber('not a number'); // Throws error
} catch (error) {
console.error(error);
}
3. Sanitização de Data
A sanitização de data envolve a validação de que as entradas de data estão no formato correto e dentro de faixas aceitáveis.
a. Validação de Formato de Data
Use expressões regulares ou bibliotecas de análise de data para garantir que uma string de data esteja em conformidade com um formato específico (por exemplo, AAAA-MM-DD).
Exemplo:
function isValidDate(dateString: string): boolean {
const dateRegex: RegExp = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(dateString)) {
return false;
}
const date: Date = new Date(dateString);
return !isNaN(date.getTime());
}
function parseDate(dateString: string): Date {
if (!isValidDate(dateString)) {
throw new Error('Invalid date format: Date must be in YYYY-MM-DD format.');
}
return new Date(dateString);
}
try {
const validDate: Date = parseDate('2023-10-27');
console.log(validDate); // Output: Fri Oct 27 2023 00:00:00 GMT+0000 (Coordinated Universal Time)
const invalidDate: Date = parseDate('2023/10/27'); // Throws error
} catch (error) {
console.error(error);
}
b. Validação de Faixa de Data
Garanta que uma data esteja dentro de uma faixa específica, como uma data de início e uma data de término.
Exemplo:
function isDateWithinRange(date: Date, startDate: Date, endDate: Date): boolean {
return date >= startDate && date <= endDate;
}
function validateDateRange(dateString: string, startDateString: string, endDateString: string): Date {
const date: Date = parseDate(dateString);
const startDate: Date = parseDate(startDateString);
const endDate: Date = parseDate(endDateString);
if (!isDateWithinRange(date, startDate, endDate)) {
throw new Error('Invalid date: Date must be between the start and end dates.');
}
return date;
}
try {
const validDate: Date = validateDateRange('2023-10-27', '2023-01-01', '2023-12-31');
console.log(validDate); // Output: Fri Oct 27 2023 00:00:00 GMT+0000 (Coordinated Universal Time)
const invalidDate: Date = validateDateRange('2024-01-01', '2023-01-01', '2023-12-31'); // Throws error
} catch (error) {
console.error(error);
}
4. Sanitização de Array
A sanitização de array envolve a validação dos elementos dentro de um array para garantir que eles atendam a critérios específicos.
a. Guardas de Tipo para Elementos de Array
Use guardas de tipo para garantir que cada elemento em um array seja do tipo esperado.
Exemplo:
function isStringArray(arr: any[]): arr is string[] {
return arr.every((item) => typeof item === 'string');
}
function processStringArray(arr: any[]): string[] {
if (!isStringArray(arr)) {
throw new Error('Invalid input: Array must contain only strings.');
}
return arr;
}
try {
const validArray: string[] = processStringArray(['apple', 'banana', 'cherry']);
console.log(validArray); // Output: [ 'apple', 'banana', 'cherry' ]
const invalidArray: string[] = processStringArray(['apple', 123, 'cherry']); // Throws error
} catch (error) {
console.error(error);
}
b. Sanitizando Elementos de Array
Aplique técnicas de sanitização a cada elemento em um array para prevenir ataques de injeção.
Exemplo:
function sanitizeStringArray(arr: string[]): string[] {
return arr.map(escapeHtml);
}
const inputArray: string[] = ['', 'normal text'];
const sanitizedArray: string[] = sanitizeStringArray(inputArray);
console.log(sanitizedArray);
// Output: [ '<script>alert("XSS");</script>', 'normal text' ]
5. Sanitização de Objeto
A sanitização de objeto envolve a validação das propriedades de um objeto para garantir que elas atendam a critérios específicos.
a. Asserções de Tipo para Propriedades de Objeto
Use asserções de tipo para impor os tipos das propriedades de objeto.
Exemplo:
interface User {
name: string;
age: number;
email: Email;
}
function validateUser(user: any): User {
if (typeof user.name !== 'string') {
throw new Error('Invalid user: Name must be a string.');
}
if (typeof user.age !== 'number') {
throw new Error('Invalid user: Age must be a number.');
}
if (typeof user.email !== 'string' || !isValidEmail(user.email)) {
throw new Error('Invalid user: Email must be a valid email address.');
}
return {
name: user.name,
age: user.age,
email: createEmail(user.email)
};
}
try {
const validUser: User = validateUser({
name: 'John Doe',
age: 30,
email: 'john.doe@example.com',
});
console.log(validUser);
// Output: { name: 'John Doe', age: 30, email: [Email: john.doe@example.com] }
const invalidUser: User = validateUser({
name: 'John Doe',
age: '30',
email: 'invalid-email',
}); // Throws error
} catch (error) {
console.error(error);
}
b. Sanitizando Propriedades de Objeto
Aplique técnicas de sanitização a cada propriedade de um objeto para prevenir ataques de injeção.
Exemplo:
interface Product {
name: string;
description: string;
price: number;
}
function sanitizeProduct(product: Product): Product {
return {
name: escapeHtml(product.name),
description: escapeHtml(product.description),
price: product.price,
};
}
const inputProduct: Product = {
name: '',
description: 'This is a product description with some HTML.',
price: 99.99,
};
const sanitizedProduct: Product = sanitizeProduct(inputProduct);
console.log(sanitizedProduct);
// Output: { name: '<script>alert("XSS");</script>', description: 'This is a product description with some <b>HTML</b>.', price: 99.99 }
Melhores Práticas para Sanitização de Entrada em TypeScript
- Sanitize cedo: Sanitize os dados o mais próximo possível da fonte de entrada.
- Use uma abordagem de defesa em profundidade: Combine a sanitização de entrada com outras medidas de segurança, como codificação de saída e consultas parametrizadas.
- Mantenha a lógica de sanitização atualizada: Mantenha-se informado sobre as últimas vulnerabilidades de segurança e atualize sua lógica de sanitização de acordo.
- Teste sua lógica de sanitização: Teste minuciosamente sua lógica de sanitização para garantir que ela previna efetivamente ataques de injeção.
- Use bibliotecas estabelecidas: Aproveite bibliotecas bem mantidas e confiáveis para tarefas comuns de sanitização, em vez de reinventar a roda. Por exemplo, considere usar uma biblioteca como validator.js.
- Considere a Localização: Ao lidar com a entrada do usuário de diferentes regiões, esteja ciente dos diferentes conjuntos de caracteres e padrões de codificação (por exemplo, UTF-8). Garanta que sua lógica de sanitização lide corretamente com essas variações para evitar a introdução de vulnerabilidades relacionadas a problemas de codificação.
Exemplos de Considerações de Entrada Global
Ao desenvolver aplicações para um público global, é crucial considerar diversos formatos de entrada e convenções culturais. Aqui estão alguns exemplos:
- Formatos de Data: Diferentes regiões usam diferentes formatos de data (por exemplo, MM/DD/AAAA nos EUA, DD/MM/AAAA na Europa). Garanta que sua aplicação possa lidar com múltiplos formatos de data e forneça validação apropriada.
- Formatos de Número: Diferentes regiões usam diferentes separadores para pontos decimais e milhares (por exemplo, 1,000.00 nos EUA, 1.000,00 na Europa). Use bibliotecas apropriadas de análise e formatação para lidar com essas variações.
- Símbolos de Moeda: Os símbolos de moeda variam entre os países (por exemplo, $, €, £). Use uma biblioteca de formatação de moeda para exibir valores de moeda corretamente com base na localidade do usuário.
- Formatos de Endereço: Os formatos de endereço variam significativamente entre os países. Forneça campos de entrada flexíveis e lógica de validação para acomodar diferentes estruturas de endereço.
- Formatos de Nome: Os formatos de nome diferem entre as culturas (por exemplo, nomes ocidentais geralmente têm um nome próprio seguido de um sobrenome, enquanto algumas culturas asiáticas invertem a ordem). Considere permitir que os usuários especifiquem sua ordem de nome preferida.
Conclusão
A sanitização de entrada é um aspecto crítico na construção de aplicações TypeScript seguras e confiáveis. Ao aproveitar o sistema de tipos do TypeScript e implementar padrões de tipo de sanitização apropriados, os desenvolvedores podem reduzir significativamente o risco de vulnerabilidades de segurança, como XSS e ataques de injeção. Lembre-se de sanitizar cedo, usar uma abordagem de defesa em profundidade e manter-se informado sobre as últimas ameaças de segurança. Seguindo essas melhores práticas, você pode construir aplicações mais robustas e seguras que protegem seus usuários e seus dados. Ao construir aplicações globais, sempre mantenha as convenções culturais em mente para garantir uma experiência de usuário positiva.
Este guia fornece uma base sólida para entender e implementar a sanitização de entrada em TypeScript. No entanto, a segurança é um campo em constante evolução. Mantenha-se sempre atualizado sobre as últimas melhores práticas e vulnerabilidades para proteger suas aplicações de forma eficaz.